#ifndef __M_OUTFORMAT_H__
#define __M_OUTFORMAT_H__

#include <iostream>
#include <vector>
#include <sstream>
#include <assert.h>
#include <time.h>
#include "Level.hpp"
#include "LogMessage.hpp"

/*
    对日志消息进行格式化，组织成为指定格式的字符串

    设计思想:
        1.抽象一个格式化子项基类
        2.基于基类,派生出不同的格式化子项子类
*/

namespace log
{
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual ~FormatItem() {}
        virtual void Format(std::ostream &os, log::LogMessage &logs) = 0;
    };
    class FileNameFormatItem : public FormatItem
    {
    public:
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            os << logs._FileName;
        }
    };
    class LogerFormatItem : public FormatItem
    {
    public:
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            os << logs._LogerName;
        }
    };
    class LineFormatItem : public FormatItem
    {
    public:
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            os << logs._Line;
        }
    };
    class LevelFormatItem : public FormatItem
    {
    public:
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            os << log::LogLevel::GetLevelString(logs._level);
        }
    };
    class ThreadFormatItem : public FormatItem
    {
    public:
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            os << logs._tid;
        }
    };
    class TimeFormatItem : public FormatItem
    {
    public:
        TimeFormatItem(std::string format = "%H:%M:%S")
            : _format(format)
        {
            if (format.empty())
                _format = "%H:%M:%S";
        }
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            time_t _time = logs._ctime;
            struct tm t;
            localtime_r(&_time, &t); // 将时间戳去填充一个结构体，结构体里面是时、分、秒等变量
            char tmp[128];
            strftime(tmp, 127, _format.c_str(), &t); // 函数strftime()的操作有些类似于sprintf()：识别以百分号(%)开始的格式命令集合，格式化输出结果放在一个字符串中。
            os << tmp;
        }

    private:
        std::string _format;
    };
    class LogMessageFormatItem : public FormatItem
    {
    public:
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            os << logs._LogMessage;
        }
    };
    class TabFormatItem : public FormatItem
    {
    public:
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            os << "\t";
        }
    };
    class NLineFormatItem : public FormatItem
    {
    public:
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            os << "\n";
        }
    };
    class OtherFormatItem : public FormatItem
    {
    public:
        OtherFormatItem(std::string message)
            : _message(message)
        {
        }
        virtual void Format(std::ostream &os, log::LogMessage &logs)
        {
            os << _message;
        }

    private:
        std::string _message;
    };

    class Format
    {
    public:
        using ptr = std::shared_ptr<Format>;
        /*
           %d ⽇期
           %T 缩进
           %t 线程id
           %p ⽇志级别
           %c ⽇志器名称
           %f ⽂件名
           %l ⾏号
           %m ⽇志消息
           %n 换⾏
       */
        Format(const std::string &format = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n")
            : _format(format)
        {
            assert(ParseFormat()); // 调用解析格式化字符函数，只能成功
        }

        const std::string GetFormat() { return _format; }

        std::string format(log::LogMessage &logs)
        {
            std::stringstream ss;  // 作为输入
            for (auto &it : _item) // 多态
            {
                it->Format(ss, logs);
            }
            return ss.str(); // 返回字符串
        }

        std::ostream &format(std::ostream &os, log::LogMessage &logs)
        {
            for (auto &it : _item) // 多态
            {
                it->Format(os, logs);
            }
            return os;
        }

        /*
           %d ⽇期
           %T 缩进
           %t 线程id
           %p ⽇志级别
           %c ⽇志器名称
           %f ⽂件名
           %l ⾏号
           %m ⽇志消息
           %n 换⾏
       */
        log::FormatItem::ptr CreateItem(std::string &key, std::string &val)
        {
            if (key == "d")
                return log::FormatItem::ptr(new TimeFormatItem(val));
            if (key == "T")
                return log::FormatItem::ptr(new TabFormatItem());
            if (key == "t")
                return log::FormatItem::ptr(new ThreadFormatItem());
            if (key == "p")
                return log::FormatItem::ptr(new LevelFormatItem());
            if (key == "c")
                return log::FormatItem::ptr(new LogerFormatItem());
            if (key == "f")
                return log::FormatItem::ptr(new FileNameFormatItem());
            if (key == "l")
                return log::FormatItem::ptr(new LineFormatItem());
            if (key == "m")
                return log::FormatItem::ptr(new LogMessageFormatItem());
            if (key == "n")
                return log::FormatItem::ptr(new NLineFormatItem());
            if (key == "")
                return log::FormatItem::ptr(new OtherFormatItem(val));
            std::cout << "没有对应的格式化字符串: %" << key << std::endl;
            abort(); // 中止程序运行,从调用的地方跳出
        }

        bool ParseFormat() // 解析格式字符串
        {
            std::string format_key;                                 // 存储%后面的格式化字符串
            std::string format_val;                                 // 存储格式化字符串后面的{}中的子格式字符串
            std::string string_row;                                 // 存储非格式化字符串
            std::vector<std::pair<std::string, std::string>> array; // 存储解析完成后的数据,比如l 行号
            bool sub_format_err = false;
            int pos = 0;
            while (pos < _format.size())
            {
                if (_format[pos] != '%')
                {
                    string_row.push_back(_format[pos++]);
                    continue;
                }
                // 走到这里，_format[pos]一定是%,如果是%%,那么格式化字符串应该被处理为%
                if (pos + 1 < _format.size() && _format[pos + 1] == '%')
                {
                    string_row.push_back('%');
                    pos += 2;
                    continue;
                }
                if (!string_row.empty())
                {
                    array.push_back(std::make_pair("", string_row));
                    string_row.clear();
                }
                pos += 1;                                          // 到达这里_format[pos]一定是%,所以要加一
                if (pos < _format.size() && isalpha(_format[pos])) // isalpha判断是不是字母
                {
                    format_key.push_back(_format[pos++]);
                }
                else // 格式化错误
                {
                    std::cerr << "格式化错误" << std::endl;
                    return false;
                }
                if (pos < _format.size() && _format[pos] == '{') // 含有子格式字符串
                {
                    sub_format_err = true;
                    pos += 1;
                    while (pos < _format.size()) // 存储子格式字符串
                    {
                        if (_format[pos] == '}')
                        {
                            sub_format_err = false;
                            pos += 1;
                            break;
                        }
                        format_val.append(1, _format[pos++]); // 存储进去一个_format[pos]
                    }
                }
                array.push_back(std::make_pair(format_key, format_val)); // 存储,格式化字符串与格式化子字符串或者格式化字符串和""
                format_key.clear();
                format_val.clear();
            }
            if (sub_format_err) // 只有{,没有}
            {
                std::cerr << "格式化错误" << std::endl;
                return false;
            }
            for (auto &it : array) // 通过array初始化_item,再通过format函数可以格式化输出
            {
                _item.push_back(CreateItem(it.first, it.second));
            }
            return true;
        }

    private:
        std::string _format;
        std::vector<std::shared_ptr<FormatItem>> _item;
    };
}

#endif